use git2::{Repository, Status};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// git status实体
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GitStatus {
    /// 文件路径
    ///
    /// ## 示例: Some("test1/test2/test.txt")
    pub path: Option<String>,
    /// 文件状态
    ///
    /// ## 示例: Status(INDEX_NEW)
    #[serde(serialize_with = "serialize_status")]
    #[serde(deserialize_with = "deserialize_status")]
    pub status: Status,
}
/// 对git2::Status的序列化实现
fn deserialize_status<'de, D>(deserializer: D) -> Result<Status, D::Error>
where
    D: Deserializer<'de>,
{
    let value: &str = Deserialize::deserialize(deserializer)?;
    match value {
        "CURRENT" => Ok(Status::CURRENT),
        "INDEX_NEW" => Ok(Status::INDEX_NEW),
        "INDEX_MODIFIED" => Ok(Status::INDEX_MODIFIED),
        "INDEX_DELETED" => Ok(Status::INDEX_DELETED),
        "INDEX_RENAMED" => Ok(Status::INDEX_RENAMED),
        "INDEX_TYPECHANGE" => Ok(Status::INDEX_TYPECHANGE),
        "WT_NEW" => Ok(Status::WT_NEW),
        "WT_MODIFIED" => Ok(Status::WT_MODIFIED),
        "WT_DELETED" => Ok(Status::WT_DELETED),
        "WT_TYPECHANGE" => Ok(Status::WT_TYPECHANGE),
        "WT_RENAMED" => Ok(Status::WT_RENAMED),
        "IGNORED" => Ok(Status::IGNORED),
        "CONFLICTED" => Ok(Status::CONFLICTED),
        _ => Err(serde::de::Error::custom("invalid status")),
    }
}
/// 对git2::Status的反序列化实现
fn serialize_status<S>(status: &Status, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_str(match *status {
        Status::CURRENT => "CURRENT",
        Status::INDEX_NEW => "INDEX_NEW",
        Status::INDEX_MODIFIED => "INDEX_MODIFIED",
        Status::INDEX_DELETED => "INDEX_DELETED",
        Status::INDEX_RENAMED => "INDEX_RENAMED",
        Status::INDEX_TYPECHANGE => "INDEX_TYPECHANGE",
        Status::WT_NEW => "WT_NEW",
        Status::WT_MODIFIED => "WT_MODIFIED",
        Status::WT_DELETED => "WT_DELETED",
        Status::WT_TYPECHANGE => "WT_TYPECHANGE",
        Status::WT_RENAMED => "WT_RENAMED",
        Status::IGNORED => "IGNORED",
        Status::CONFLICTED => "CONFLICTED",
        _ => "other",
    })
}

/// git status
pub fn git_status(repo: &Repository) -> anyhow::Result<Vec<GitStatus>> {
    let statuses = repo.statuses(None)?;
    let mut r = vec![];
    for x in statuses.iter() {
        let path = x.path().and_then(|r| Some(String::from(r)));
        let status = GitStatus {
            path,
            status: x.status(),
        };
        r.push(status);
    }
    Ok(r)
}
